#ifndef _OBJECTS_H_
#define _OBJECTS_H_

#include <cutil_math.h>
#include "common.h"

// base class for scene objects
// vector_t: float2, float3, float4...
template <class vector_t>
class base_abstract_object
{
protected:
    // color of an object where it is in shadow
	   const vector_t __ambient_color;

public:
    __device__ base_abstract_object(const vector_t color)
        : __ambient_color(color)
    {
    }
    __device__ vector_t get_ambient_color() const
		  { 
        return __ambient_color;
    }

    virtual __device__ bool intersect(const vector_t ray_origin, const vector_t ray_direction, vector_t *res) = 0;
};


template <class vector_t, class vector_value_t>
class sphere : public base_abstract_object <vector_t>
{
private:
    vector_t       __sphere_position;
    vector_value_t __sphere_radius;
    
public:
    __device__ sphere(const vector_t pos, const vector_value_t r, const vector_t color)
        : base_abstract_object <vector_t>(color),
          __sphere_position(pos),
          __sphere_radius(r)
    {
    }
    
    __device__ bool intersect(const vector_t ray_pos, const vector_t ray_dir, vector_t *res)
    {
        //  normalize direction
        vector_t dir = ray_dir * 1.0f / sqrt(ray_dir.x*ray_dir.x + ray_dir.y*ray_dir.y + ray_dir.z*ray_dir.z);
        vector_t k = ray_pos - __sphere_position;
        vector_value_t b = dot(k, dir);
        
        // calculate descriminant
        vector_value_t d = b * b - dot(k, k) + __sphere_radius * __sphere_radius;

        if(d > 0.00001f)
        {
            vector_value_t sqrtfd = sqrtf(d);
            // t, a == 1
            vector_value_t t1 = -b + sqrtfd;
            vector_value_t t2 = -b - sqrtfd;
        
            if (((t1 - 1) < 0.00001f)) return false;
        
            vector_value_t t = (t2 >= 0.00001f) ? t2 : t1;
       

            *res = ray_pos + t * dir;
            return true;
            
        }
        
        return false;    
    }
};


template <class vector_t, class vector_value_t>
class triangle : public base_abstract_object <vector_t>
{
private:
    vector_t __vertex_a;
    vector_t __vertex_b;
    vector_t __vertex_c;
    
public:
    __device__ triangle(const vector_t pos_a, const vector_t pos_b, const vector_t pos_c, const vector_t color)
        : base_abstract_object <vector_t>(color),        
          __vertex_a(pos_a),
          __vertex_b(pos_b),
          __vertex_c(pos_c)
    {
    }
    
    __device__ bool intersect(const vector_t ray_pos, const vector_t ray_dir, vector_t *res)
    {
        //  normalize direction
        vector_t dir = ray_dir * 1.0f / sqrt(ray_dir.x*ray_dir.x + ray_dir.y*ray_dir.y + ray_dir.z*ray_dir.z);
        
        vector_t e1 = __vertex_b - __vertex_a;
        vector_t e2 = __vertex_c - __vertex_a;
        vector_t h  = cross(dir, e2);
        
        vector_value_t a = dot(e1, h);
        if (a > -0.00001f && a < 0.00001f) return false;

        a = 1 / a;
        vector_t s = ray_pos - __vertex_a;
        vector_value_t u = a * dot(s, h);
        if (u < 0.0f || u > 1.0f) return false;

        vector_t q = cross(s, e1);
        vector_value_t v = a * dot(dir, q);
        if (v < 0.0f || ((u + v) > 1.0f)) return false;

        vector_value_t t = a * dot(e2, q);

        if (t > 1.00001f)
        {
            *res = ray_pos + t * dir;
            return true;
        }

        return false;    
    }
};











/*





template <class vector_t, class vector_value_t>
class flat
{
public:
    vector_t normal;
    vector_value_t d;
    
    __device__ flat(const vector_t n, const vector_value_t _d)
        : normal(n),
          d(_d)
          {
          }
          
    __device__ bool intersect(vector_t ray_pos,vector_t ray_dir, vector_t *res)
    {
        ray_dir = ray_dir * 1.0f / sqrt(ray_dir.x*ray_dir.x + ray_dir.y*ray_dir.y + ray_dir.z*ray_dir.z);
        vector_value_t tmp1 = d - dot(ray_pos, normal);
        vector_value_t tmp2 = dot(ray_dir, normal);
        
        if (tmp2 == 0.0f) return false;
        vector_value_t t = tmp1 / tmp2;
        *res = ray_pos + t * ray_dir;
        
        return true;
    }
};

*/
#endif